package com.emmmya.ocs.modules.system.controller;


import com.alibaba.fastjson.JSON;
import com.emmmya.ocs.common.constant.RSAConstant;
import com.emmmya.ocs.common.constant.RedisConstant;
import com.emmmya.ocs.common.utils.RSA.RSAUtils;
import com.emmmya.ocs.common.utils.ResultUtil;
import com.emmmya.ocs.common.vo.Result;
import com.emmmya.ocs.config.redis.RedisManager;
import com.emmmya.ocs.config.security.shiro.NoPwdUsernameToken;
import com.emmmya.ocs.modules.system.annotation.OperateLog;
import com.emmmya.ocs.modules.system.entity.User;
import com.emmmya.ocs.modules.system.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

@Slf4j
@RestController
// "使用shiro+jwt来控制登录"
public class TokenController {
    @Autowired
    private RedisManager redisManager;

    @Autowired
    private RedisManager shiroRedisManager;

    @Autowired
    private UserService userService;


    @Value("${config.jwt.header}")
    private String header;

    /**
     * 调用RSA工具类的getModulus方法获取配套的公钥，秘钥，和加密模块信息，并将公钥和加密模块传送到前台，秘钥和加密模块
     * 保存到后台
     */
    @RequestMapping(value = "/getMoudle", method = RequestMethod.GET)
    public Result<Object> getMoudle() throws NoSuchAlgorithmException {
        Map jmInfo = RSAUtils.getModulus();
        UUID uuid = UUID.randomUUID();
        redisManager.set(RedisConstant.RSA_TOKEN+uuid,JSON.toJSON(jmInfo),900);
        jmInfo.put("key",uuid);
        jmInfo.remove(RSAConstant.PRIVATE_EXPONENT);
        return ResultUtil.data(jmInfo);
    }


    /**
     * 用户登录接口
     * @return
     */
    @RequestMapping (value = "/user/login")
    @OperateLog(value="用户登录",logType= 1,module = "系统登录")
    public Result<Object> login (HttpServletResponse response,
                                 @RequestParam("userCip")String userCip,
                                 @RequestParam("rsaKey")String rsaKey){
        User user = null;
        String loginToken = null;
        //
        try {
            //获取RSA组
            Object jmInfoStr = redisManager.get(RedisConstant.RSA_TOKEN + rsaKey);
            if(jmInfoStr == null){
                return ResultUtil.error("当前页面已失效，请刷新后重新登录！");
            }
            redisManager.del(RedisConstant.RSA_TOKEN + rsaKey);
            Map parse = JSON.parseObject(jmInfoStr.toString());
            String private_exponent = (String)parse.get(RSAConstant.PRIVATE_EXPONENT);
            String modulus = (String)parse.get(RSAConstant.MODULUS);
            RSAPrivateKey privateKey = RSAUtils.getPrivateKey(modulus, private_exponent);
            String userJson = RSAUtils.parse(userCip, privateKey);
            //String userJson = RSAUtilV2.decryptByPrivate(encode,null);
            user = JSON.toJavaObject(JSON.parseObject(userJson),User.class);

            loginToken = user.getLoginToken();
            //System.out.println("loginToken:"+loginToken);
            if(StringUtils.isEmpty(loginToken)){
                response.sendRedirect("/login");
                return null;
            }
            String uuid = (String) redisManager.get(RedisConstant.LOGIN_TOKEN + loginToken);
            if(!"true".equals(uuid)){
                response.sendRedirect("/login");
                return  null;
                //return ResultUtils.error(210,"登陆token无效，请返回登陆页面重新登陆！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String username = user.getUsername();
        String password = user.getPassword();
        String token = null;
        //使用shiro框架验证user
        Subject subject = SecurityUtils.getSubject();

        //String redisKeyUser = (String)subject.getPrincipal();
        User currentUser = (User)subject.getPrincipal();

        //清除redis的选项
        redisManager.del(RedisConstant.REDIS_USER_TOKEN + username);
        //if(!StringUtils.isEmpty(redisKeyUser)){
        if(currentUser != null) {
            subject.logout();
            //Boolean del = redisManager.del(RedisConstant.REDIS_USER_TOKEN + jwtConfig.getUsernameFromToken(token));
            //修改项
            //再一次清除
            Boolean del = redisManager.del( RedisConstant.REDIS_USER_TOKEN + currentUser.getUsername() );
        }
        //如果没有授权，则需要登录
        if (!subject.isAuthenticated()) {
            NoPwdUsernameToken usernamePasswordToken =
                    new NoPwdUsernameToken(username,password);
            if(!StringUtils.isEmpty(user.getRememberMe())) {
                usernamePasswordToken.setRememberMe(true);
            }
            //这个token应该在shiro登录时生成。
            try {
                //进行验证，这里可以捕获异常，然后返回对应信息
                subject.login(usernamePasswordToken);
                //redisKeyUser = (String) subject.getPrincipal();
                currentUser = (User) subject.getPrincipal();

                //if(!StringUtils.isEmpty(redisKeyUser)) {
                //重试登录一次。
                if(currentUser == null){
                    subject.login(usernamePasswordToken);
                    //token = (String) redisManager.get(RedisConstant.REDIS_USER_TOKEN+currentUser.getUsername());
                } else {
//                    token = (String) redisManager.get(redisKeyUser);
                    token = (String) redisManager.get(RedisConstant.REDIS_USER_TOKEN + username);
                    if(StringUtils.isEmpty(token)){
                        for(int i=0; i<5; i++) {
                            //先登出
                            subject.logout();
                            //再登录
                            subject.login(usernamePasswordToken);
                            currentUser = (User) subject.getPrincipal();

                            //if(!StringUtils.isEmpty(redisKeyUser)) {
                            if(currentUser != null){
                                token = (String) redisManager.get(RedisConstant.REDIS_USER_TOKEN+currentUser.getUsername());
                            }

                            if(!StringUtils.isEmpty(token)){
                                break;
                            }
                        }
                    }
                }

                Boolean del = redisManager.del(RedisConstant.LOGIN_TOKEN + loginToken);
                if (del) {
                    log.info("redis消费了" + RedisConstant.LOGIN_TOKEN + loginToken);
                }
                user = userService.findByUsername(user.getUsername());
                //user.setLastLoginIp(IpUtils.getIpAddr(request));
                user.setLastLoginTime(new Date());
                userService.updateEnhance(user);
                Session session = subject.getSession();
                session.setAttribute("currentUser",userService.getUserById(user.getId()));
            }
            catch(IncorrectCredentialsException e){
                e.printStackTrace();
                return ResultUtil.error(210, "账号不存在或密码错误！");
            }
            catch (AuthenticationException e) {
                e.printStackTrace();
                return ResultUtil.error(210, e.getMessage());
            } catch (AuthorizationException e) {
                e.printStackTrace();
                return ResultUtil.error(401, "没有权限");
            }catch (Exception e){
                e.printStackTrace();
                return ResultUtil.error(500, "系统错误，请重新尝试！");
            }
        }
        //MutableHttpServletRequest mrequest = new MutableHttpServletRequest(request);
        //mrequest.putHeader(header,token);

        //mrequest.getRequestDispatcher("/jump").forward(mrequest,response);
        //转发后，这里只能返回null;
        //return null;
        return ResultUtil.data(token);
    }


    /**
     * 获取token，并登出
     */
    @RequestMapping (value = "/user/logout")
    @RequiresAuthentication
    @OperateLog(value="用户登出",logType= 1,module = "系统登录")
    //@RequiresPermissions(value = {"put"},logical= Logical.OR)
    public Result<Object> logout(HttpServletRequest request){
        try {
            String token = request.getHeader(this.header);
            Subject subject = SecurityUtils.getSubject();
//            User user = (User)subject.getPrincipal();
//            String redisKeyUser = "";
//            if((StringUtils.isEmpty(token) || "null".equals(token)) && user != null){
//                redisKeyUser = RedisConstant.REDIS_USER_TOKEN + user.getUsername();
//                token = (String)shiroRedisManager.get(redisKeyUser);
//            }
//            System.out.println("logout:"+token);
            subject.logout();
            //Boolean del = redisManager.del(RedisConstant.REDIS_USER_TOKEN + jwtConfig.getUsernameFromToken(token));
            //移除
            //Boolean del = shiroRedisManager.del( redisKeyUser );
            return ResultUtil.success("登出成功！！");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultUtil.error(230,"登出失败！！");

    }
    /**
     * 需要 Token 验证的接口
     */
    @PostMapping("/testToken")
    public Result<Object> info (){
        //Object principal = SecurityUtils.getSubject().getPrincipal();
        //System.out.println(principal+"");
        return ResultUtil.success("你有token呀?NB喔。") ;
    }
 
    /**
     * 根据请求头的token获取userId
     * @param request
     * @return
     */
    @RequestMapping("/getUserInfo")
    public Result<Object>  getUserInfo(HttpServletRequest request){
        //String usernameFromToken = jwtConfig.getUsernameFromToken(request.getHeader(jwtConfig.getHeader()));
        //return ResultUtils.success(usernameFromToken) ;
        return null;
    }

    /*
        为什么项目重启后，带着之前的token还可以访问到需要info等需要token验证的接口？
        答案：只要不过期，会一直存在，类似于redis。
        不安全。只要在把时间改了，然后自己生成一个token,也可以访问。
        所以，这个token 最好也在reids放一份。判断redis是否存在，存在可以访问。
     */
 
}